/***************************************************************************
 * 
 * Copyright (c) 2011 Baidu.com, Inc. All Rights Reserved
 * common.h,v 1.0 2012-08-17 11:31 yelu01 
 * 
 **************************************************************************/
 
 
 
/**
 * @file bytes.h
 * @author yelu01(yelu01@baidu.com)
 * @date 2012-08-21 10:08
 * @version 1.0 
 * @brief array of bytes.
 *  
 **/

#pragma once

#include <cstdlib>
#include <cassert>
#include <boost/shared_array.hpp>
#include "check_macro.h"
#include "log.h"

class bytes_t
{
public:

	// Create an empty bytes_t.
	bytes_t()
	{
		_p_data.reset();
		_size = 0;
	}

	// Copy constrictor.
	bytes_t(bytes_t& r)
	{
		this->_size = r._size;
		_p_data = r._p_data;
	}

	// Create a bytes_t from uint64_t.
	bytes_t(const uint64_t data)
	{
		this->_size = sizeof(data);
		_p_data.reset(new char[_size]);
		CHECK_TRUE(_p_data, return, "failed to alloc memory.");
		memcpy(_p_data.get(), &data, _size);
	}

	// Create a bytes_t that refers to data[0,n-1].
	bytes_t(void* data, size_t n)
	{
		_size = n;
		_p_data.reset(new char[_size]);
		CHECK_TRUE(_p_data, , "failed to alloc memory.");
		memcpy(_p_data.get(), data, _size);
	}

	// Create a bytes_t that refers to s[0,strlen(s)]
	bytes_t(char* data)
	{
		_size = strlen(data) + 1;
		_p_data.reset(new char[_size]);
		CHECK_TRUE(_p_data, , "failed to alloc memory.");
		memcpy(_p_data.get(), data, _size);
	}

	operator char* () const
	{
		char* t = _p_data.get();
		return t;
	}

	operator void* () const
	{
		void* t = _p_data.get();
		return t;
	}

	template<typename T>
	operator T () const
	{
		assert(sizeof(T) == _size);
		T* p = reinterpret_cast<T*>(_p_data.get());
		T t = (*p);
		return t;
	}

	// Operator =
	bytes_t& operator = (const bytes_t& r)
	{
		this->_size = r._size;
		_p_data = r._p_data;
		return *this;
	}

	// Destroy a bytes_t.
	~bytes_t(){ _size = 0; }

	// Return the length (in bytes) of the referenced data
	uint64_t size() const { return _size; }

	// Return true if the length of the referenced data is zero
	bool empty() const { return _size <= 0; }

	// Return the ith byte in the referenced data.
	// REQUIRES: n < size()
	char operator[](size_t n) const
	{
		assert(n < size());
		const char* p = (const char*)_p_data.get();
		return p[n];
	}

	// Compare by memory.
	//   true if a < b, else false
	static bool raw_comparator(const bytes_t& a, const bytes_t& b)
	{
		const int min_len = (a._size < b._size) ? a._size : b._size;
		int r = memcmp(a._p_data.get(), b._p_data.get(), min_len);
		if(0 > r) {return true;}
		if(0 == r && a._size < b._size) {return true;}
		return false;
	}

	// Compare by int.
	// Note: raw_comparaor cannot compare int etc due to endian.
	//   true if a < b, else false
	static bool uint64_comparator(const bytes_t& a, const bytes_t& b)
	{
		assert(a._size == sizeof(uint64_t) && b._size == sizeof(uint64_t));
		uint64_t lhs = *((const uint64_t*)(a._p_data.get()));
		uint64_t rhs = *((const uint64_t*)(b._p_data.get()));
		return lhs < rhs;
	}

	uint64_t serialize(FILE* fp)
	{
		CHECK_NOT_NULL(fp, return 0, "fp is null.");
		uint64_t total_len = 0;

		// write size.
		uint64_t len = fwrite(&_size, sizeof(uint64_t), 1, fp);
		CHECK_TRUE(1 == len, return 0, "fwrite bytes_t to file failed.");
		total_len += (len * sizeof(uint64_t));

		// write data.
		len = fwrite(_p_data.get(), 1, _size, fp);
		CHECK_TRUE(_size == len, return 0, "fwrite bytes_t to file failed.");
		total_len += (len * 1);

		return total_len;
	}

	uint64_t deserialize(FILE* fp)
	{
		CHECK_NOT_NULL(fp, return 0, "fp is null.");
		uint64_t total_len = 0;

		// read size.
		uint64_t len = fread(&_size, sizeof(uint64_t), 1, fp);
		CHECK_TRUE(1 == len, return 0, "fwrite bytes_t to file failed.");
		total_len += (len * sizeof(uint64_t));

		// read data.
		_p_data.reset(new char[_size]);
		CHECK_TRUE(_p_data, return 0, "failed to alloc memory.")
		len = fread(_p_data.get(), 1, _size, fp);
		CHECK_TRUE(_size == len, return 0, "fwrite bytes_t to file failed.");
		total_len += (len * 1);

		return total_len;
	}

private:
	boost::shared_array<char> _p_data;
	uint64_t _size;
};





